/**
 * @file   Worker.java
 * @author Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Mon Jul 16 18:31:27 2007
 *
 * @brief 
 *
 */

package sim.framework;

import java.util.*;

public final class Worker extends Thread {
    
    private static final int QUEUE_INIT_SIZE = 4;

    private static final int LOCKED_FOR_APPEND = 1;
    private static final int LOCKED_FOR_REMOVE = 2;
    private static final int LOCKED_FOR_DELETE = 3;
    
    protected Simlet m_Simlet;

    // Control
    protected boolean m_Stop;
    protected boolean m_Working;

    // Tareas
    private Vector m_Pending = new Vector(QUEUE_INIT_SIZE);
    private Vector m_Recycle = new Vector(QUEUE_INIT_SIZE);

    /**
     * Constructor.
     *
     * @param s Referencia a un objeto del tipo Simlet.
     *
     * @return Devuelve una referencia a un objeto del tipo Worker.
     */
    public Worker(Simlet s) {
        m_Simlet = s;
    }
    
    private Msg newMsg() {
        Msg msg = null;
        if (m_Recycle.size() > 0) {
            msg = (Msg)m_Recycle.firstElement();
            m_Recycle.removeElement(msg);
        }
	else {
	    msg = new Msg();
	}
        return msg;
    }

    private synchronized Msg lock(int lockFor, Object obj, int state) {
	Msg msg = null;
	switch(lockFor) {
	case Worker.LOCKED_FOR_APPEND:
	    lockedForAppend((Module)obj, state);
	    break;
	case Worker.LOCKED_FOR_REMOVE:
	    msg = lockedForRemove();
	    break;
	case Worker.LOCKED_FOR_DELETE:
	    lockedForDelete((Msg)obj);
	    break;
	}
	return msg;
    }
    
    private void lockedForAppend(Module module, int state) {
        Msg msg = null;
	if (module != null) {
	    // Crear nuevo mensaje
	    msg = newMsg();
	    // Establecer los parámetros de un tarea.
	    msg.setObject(module);
	    msg.setWParam(state);
	    // Añadirlo a las lista de tareas pendientes.
	    m_Pending.addElement(msg);	
	    // Notificar a la hebra.
	    notify();
	}
    }

    private void lockedForDelete(Msg msg) {
	// Añadir el mensaje para reciclarlo en otras tareas.
	m_Recycle.addElement(msg);
    }

    private Msg lockedForRemove() {
	Msg msg = null;
	
	if (m_Pending.size() > 0) {
	    msg = (Msg)m_Pending.firstElement();
	    m_Pending.removeElement(msg);
	}
	
	return msg;
    }
           
    /**
     * Ejecuta un módulo en segundo plano.
     *
     * Ejecuta un módulo que implementa la interfaz Executable al llamar
     * a la función a la función work() de dicho módulo.
     *
     */
    public void run() {
        Msg msg;
	Module m;
	
	//#ifdef DEBUG_WORKER
	//# System.out.println("Worker::run() ENTERED");
	//#endif
        
        while(!m_Stop) {
	    //#ifdef DEBUG_WORKER
	    //# System.out.println("\tWorker::run() Iterando");
	    //#endif
	    
            msg = remove();
	    while(msg != null) {
		//#ifdef DEBUG_WORKER
		//# System.out.println("\tWorker::run() despachando");
		//#endif
		m = (Module)msg.getObject();
		//#ifdef DEBUG_WORKER
		//# System.out.println("\tMódulo: " + (m.getClass()).getName());
		//# System.out.println("\tEstado: " + msg.getWParam());
		//#endif
		try {

		    m_Working = true;
		    m.work(msg);
		    m_Working = false;
		    
		} catch (Exception e) {

		    System.out.println("\nME CAI EN WORKER\n");
		    System.out.println("\nTrace\n");
		    e.printStackTrace();
		    
		}
		
		delete(msg);
                msg = remove();
            }

	    //#ifdef DEBUG_WORKER
	    //# System.out.println("\tWorker::run() durmiendo");
	    //#endif
           
	    synchronized (this) { 
		try {
		    wait(0);
		} catch( InterruptedException e ) {
		    
		};
	    }
        }
	
	//#ifdef DEBUG_WORKER
	//# System.out.println("Worker::run() EXITING");
	//#endif
	
    }
    
    protected void append(Module module, int state) {
	lock(Worker.LOCKED_FOR_APPEND, module, state);
    }
    
    private Msg remove() {
        return lock(Worker.LOCKED_FOR_REMOVE, null, 0);
    }
    
    private void delete(Msg msg) {
	lock(Worker.LOCKED_FOR_DELETE, msg, 0);
    }
    
    protected synchronized void stop() {
        m_Stop = true;
        notify();
    }
    
}
